/* r8c.opc --- semantics for r8c opcodes.		        -*- mode: c -*-

Copyright (C) 2005-2024 Free Software Foundation, Inc.
Contributed by Red Hat, Inc.

This file is part of the GNU simulators.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.  */

/* This must come before any other includes.  */
#include "defs.h"

#include <stdio.h>
#include <stdlib.h>

#include "ansidecl.h"
#include "cpu.h"
#include "mem.h"
#include "misc.h"
#include "int.h"

#define tprintf if (trace) printf

static unsigned char
getbyte (void)
{
  int tsave = trace;
  unsigned char b;

  if (trace == 1)
    trace = 0;
  b = mem_get_pc ();
  regs.r_pc ++;
  trace = tsave;
  return b;
}

#define M16C_ONLY() /* FIXME: add something here */

#define GETBYTE() (op[opi++] = getbyte())

#define UNSUPPORTED() unsupported("unsupported", orig_pc)
#define NOTYET() unsupported("unimplemented", orig_pc)

static void
unsupported (char *tag, int orig_pc)
{
  int i;
  printf("%s opcode at %08x\n", tag, orig_pc);
  regs.r_pc = orig_pc;
  for (i=0; i<2; i++)
    {
      int b = mem_get_pc();
      printf(" %s", bits(b>>4, 4));
      printf(" %s", bits(b, 4));
      regs.r_pc ++;
    }
  printf("\n");
  regs.r_pc = orig_pc;
  for (i=0; i<6; i++)
    {
      printf(" %02x", mem_get_pc ());
      regs.r_pc ++;
    }
  printf("\n");
  exit(1);
}

static int
IMM (int bw)
{
  int rv = getbyte ();
  if (bw)
    rv = rv + 256 * getbyte();
  if (bw == 2)
    rv = rv + 65536 * getbyte();
  return rv;
}

#define IMM4() (immm >= 8 ? 7 - immm : immm + 1)

#define UNARY_SOP \
  dc = decode_srcdest4 (dest, w); \
  v = sign_ext (get_src (dc), w?16:8);

#define UNARY_UOP \
  dc = decode_srcdest4 (dest, w); \
  v = get_src (dc);

#define BINARY_SOP \
  sc = decode_srcdest4 (srcx, w); \
  dc = decode_srcdest4 (dest, w); \
  a = sign_ext (get_src (sc), w?16:8); \
  b = sign_ext (get_src (dc), w?16:8);

#define BINARY_UOP \
  sc = decode_srcdest4 (srcx, w); \
  dc = decode_srcdest4 (dest, w); \
  a = get_src (sc); \
  b = get_src (dc);

#define carry (FLAG_C ? 1 : 0)

static void
cmp (int d, int s, int w)
{
  int a, b, f=0;
  int mask = w ? 0xffff : 0xff;
  a = d - s;
  b = sign_ext (d, w?16:8) - sign_ext (s, w?16:8);
  tprintf ("cmp: %x - %x = %08x, %x - %x = %d\n",
	   d, s, a,
	   sign_ext(d,w?16:8), sign_ext(s,w?16:8), b);

  if (b == 0)
    f |= FLAGBIT_Z;
  if (b & (w ? 0x8000 : 0x80))
    f |= FLAGBIT_S;
  if ((d & mask) >= (s & mask))
    f |= FLAGBIT_C;
  if (b < (w ? -32768 : -128) || b > (w ? 32767 : 127))
    f |= FLAGBIT_O;

  set_flags (FLAGBIT_Z | FLAGBIT_S | FLAGBIT_O | FLAGBIT_C, f);
}

static void
div_op (int s, int u, int x, int w)
{
  srcdest sc;
  int v, a, b;

  if (s == -1)
    s = IMM(w);
  else
    {
      sc = decode_srcdest4 (s, w);
      s = get_src (sc);
    }

  v = get_reg (w ? r2r0 : r0);

  if (!u)
    {
      s = sign_ext (s, w ? 16 : 8);
      v = sign_ext (v, w ? 16 : 8);
    }

  if (s == 0)
    {
      set_flags (FLAGBIT_O, FLAGBIT_O);
      return;
    }

  if (u)
    {
      a = (unsigned int)v / (unsigned int)s;
      b = (unsigned int)v % (unsigned int)s;
    }
  else
    {
      a = v / s;
      b = v % s;
    }
  if (x)
    {
      if ((s > 0 && b < 0)
	  || (s < 0 && b > 0))
	{
	  a --;
	  b += s;
	}
    }
  tprintf ("%d / %d = %d rem %d\n", v, s, a, b);
  if ((!u && (a > (w ? 32767 : 127)
	     || a < (w ? -32768 : -129)))
      || (u && (a > (w ? 65536 : 255))))
    set_flags (FLAGBIT_O, FLAGBIT_O);
  else
    set_flags (FLAGBIT_O, 0);

  put_reg (w ? r0 : r0l, a);
  put_reg (w ? r2 : r0h, b);
}

static void
rot_op (srcdest sd, int rotc, int count)
{
  int mask = (sd.bytes == 2) ? 0xffff : 0xff;
  int msb = (sd.bytes == 2) ? 0x8000 : 0x80;
  int v = get_src (sd);
  int c = carry, ct;

  tprintf("%s %x by %d\n", rotc ? "rotc" : "rot", v, count);
  tprintf (": %s %d\n", bits(v, 8*sd.bytes), c);
  while (count > 0)
    {
      ct = (v & msb) ? 1 : 0;
      v <<= 1;
      v |= rotc ? c : ct;
      v &= mask;
      c = ct;
      tprintf (": %s %d\n", bits(v, 8*sd.bytes), c);
      count --;
    }
  while (count < 0)
    {
      ct = v & 1;
      v >>= 1;
      v |= (rotc ? c : ct) * msb;
      c = ct;
      tprintf (": %s %d\n", bits(v, 8*sd.bytes), c);
      count ++;
    }
  put_dest (sd, v);
  set_szc (v, sd.bytes, c);
}

static void
shift_op (srcdest sd, int arith, int count)
{
  int mask = (sd.bytes == 2) ? 0xffff : 0xff;
  int msb = (sd.bytes == 2) ? 0x8000 : 0x80;
  int v = get_src (sd);
  int c = 0;

  if (sd.bytes == 4)
    {
      mask = 0xffffffffU;
      msb = 0x80000000U;
      if (count > 16 || count < -16)
	{
	  fprintf(stderr, "Error: SI shift of %d undefined\n", count);
	  exit(1);
	}
      if (count > 16)
	count = (count - 1) % 16 + 1;
      if (count < -16)
	count = -((-count - 1) % 16 + 1);
    }

  tprintf("%s %x by %d\n", arith ? "sha" : "shl", v, count);
  tprintf (": %s %d\n", bits(v, 8*sd.bytes), c);
  while (count > 0)
    {
      c = (v & msb) ? 1 : 0;
      v <<= 1;
      v &= mask;
      tprintf (": %s %d\n", bits(v, 8*sd.bytes), c);
      count --;
    }
  while (count < 0)
    {
      c = v & 1;
      if (arith)
	v = (v & msb) | (v >> 1);
      else
	v = (v >> 1) & (msb - 1);
      tprintf (": %s %d\n", bits(v, 8*sd.bytes), c);
      count ++;
    }
  put_dest (sd, v);
  set_szc (v, sd.bytes, c);
}

#define MATH_OP(dc,s,c,op,carryrel) \
  a = get_src(dc); \
  b = s & b2mask[dc.bytes]; \
  v2 = a op b op c; \
  tprintf("0x%x " #op " 0x%x " #op " 0x%x = 0x%x\n", a, b, c, v2); \
  a = sign_ext (a, dc.bytes * 8); \
  b = sign_ext (s, dc.bytes * 8); \
  v = a op b op c; \
  tprintf("%d " #op " %d " #op " %d = %d\n", a, b, c, v); \
  set_oszc (v, dc.bytes, v2 carryrel); \
  put_dest (dc, v2);

#define BIT_OP(field,expr) \
  dc = decode_bit (field); \
  b = get_bit (dc); \
  v = expr; \
  tprintf ("b=%d, carry=%d, %s = %d\n", b, carry, #expr, v); \
  put_bit (dc, v);

#define BIT_OPC(field,expr) \
  dc = decode_bit (field); \
  b = get_bit (dc); \
  v = expr; \
  tprintf ("b=%d, carry=%d, %s = %d\n", b, carry, #expr, v); \
  set_c (v);

/* The "BMcnd dest" opcode uses a different encoding for the */
/* condition than other opcodes.  */
static int bmcnd_cond_map[] = {
  0, 1, 2, 3, 8, 9, 10, 11, 4, 5, 6, 7, 12, 13, 14, 15
};

int
decode_r8c (void)
{
  unsigned char op[40];
  int opi = 0;
  int v, v2, a, b;
  int orig_pc = get_reg (pc);
  srcdest sc, dc;
  int imm;

  step_result = M32C_MAKE_STEPPED ();

  tprintf("trace: decode pc = %05x\n", orig_pc);

  /** VARY dst 011 100 101 110 111 */

  /** 0111 011w 1111 dest  ABS.size dest */

  UNARY_SOP;
  a = v<0 ? -v : v;
  tprintf("abs(%d) = %d\n", v, a);
  set_osz(a, w+1);
  put_dest (dc, a);

  /** 0111 011w 0110 dest  ADC.size #IMM,dest */

  dc = decode_srcdest4(dest, w);
  imm = IMM(w);
  MATH_OP (dc, imm, carry, +, > (w?0xffff:0xff));

  /** 1011 000w srcx dest  ADC.size src,dest */

  sc = decode_srcdest4(srcx, w);
  dc = decode_srcdest4(dest, w);
  b = get_src (sc);
  MATH_OP (dc, b, carry, +, > (w?0xffff:0xff));

  /** 0111 011w 1110 dest  ADCF.size dest */

  dc = decode_srcdest4(dest, w);
  MATH_OP (dc, 0, carry, +, > (w?0xffff:0xff));

  /** 0111 011w 0100 dest  ADD.size:G #imm,dest */

  dc = decode_srcdest4(dest, w);
  imm = IMM(w);
  MATH_OP (dc, imm, 0, +, > (w?0xffff:0xff));

  /** 1100 100w immm dest  ADD.size:Q #IMM,dest */

  dc = decode_srcdest4(dest, w);
  imm = sign_ext (immm, 4);
  MATH_OP (dc, imm, 0, +, > (w?0xffff:0xff));

  /** 1000 0dst            ADD.B:S #IMM8,dst */

  imm = IMM(0);
  dc = decode_dest3 (dst, 0);
  MATH_OP (dc, imm, 0, +, > 0xff);

  /** 1010 000w srcx dest  ADD.size:G src,dest */

  sc = decode_srcdest4(srcx, w);
  dc = decode_srcdest4(dest, w);
  b = get_src (sc);
  MATH_OP (dc, b, 0, +, > (w?0xffff:0xff));

  /** 0010 0d sr           ADD.B:S src,R0L/R0H */

  sc = decode_src2 (sr, 0, d);
  dc = decode_dest1 (d, 0);
  b = get_src (sc);
  MATH_OP (dc, b, 0, +, > 0xff);

  /** 0111 110w 1110 1011  ADD.size:G #IMM,sp */

  dc = reg_sd (sp);
  imm = sign_ext (IMM(w), w?16:8);
  MATH_OP (dc, imm, 0, +, > 0xffff);

  /** 0111 1101 1011 immm  ADD.size:Q #IMM,sp */

  dc = reg_sd (sp);
  imm = sign_ext (immm, 4);
  MATH_OP (dc, imm, 0, +, > 0xffff);

  /** 1111 100w immm dest  ADJNZ.size #IMM,dest,label */

  UNARY_UOP;
  imm = sign_ext(immm, 4);
  tprintf("%x + %d = %x\n", v, imm, v+imm);
  v += imm;
  put_dest (dc, v);
  a = sign_ext (IMM(0), 8);
  if ((v & (w ? 0xffff : 0xff)) != 0)
    {
      tprintf("jmp: %x + 2 + %d = ", get_reg (pc), a);
      put_reg (pc, orig_pc + 2 + a);
      tprintf("%x\n", get_reg (pc));
    }

  /** 0111 011w 0010 dest  AND.size:G #IMM,dest */

  UNARY_UOP;
  imm = IMM(w);
  tprintf ("%x & %x = %x\n", v, imm, v & imm);
  v &= imm;
  set_sz (v, w+1);
  put_dest (dc, v);

  /** 1001 0dst            AND.B:S #IMM8,dest */

  imm = IMM(0);
  dc = decode_dest3 (dst, 0);
  v = get_src (dc);
  tprintf("%x & %x = %x\n", v, imm, v & imm);
  v &= imm;
  set_sz (v, 1);
  put_dest (dc, v);

  /** 1001 000w srcx dest  AND.size:G src.dest */

  BINARY_UOP;
  tprintf ("%x & %x = %x\n", a, b, a & b);
  v = a & b;
  set_sz (v, w+1);
  put_dest (dc, v);

  /** 0001 0d sr           AND.B:S src,R0L/R0H */

  sc = decode_src2 (sr, 0, d);
  dc = decode_dest1 (d, 0);
  a = get_src (sc);
  b = get_src (dc);
  v = a & b;
  tprintf("%x & %x = %x\n", a, b, v);
  set_sz (v, 1);
  put_dest (dc, v);

  /** 0111 1110 0100 srcx  BAND src */

  BIT_OPC (srcx, b & carry);

  /** 0111 1110 1000 dest  BCLR:G dest */

  dc = decode_bit (dest);
  put_bit (dc, 0);

  /** 0100 0bit            BCLR:S bit,base:11[SB] */

  dc = decode_bit11 (bit);
  put_bit (dc, 0);

  /** 0111 1110 0010 dest  BMcnd dest  */

  dc = decode_bit (dest);
  if (condition_true (bmcnd_cond_map [IMM (0) & 15]))
    put_bit (dc, 1);
  else
    put_bit (dc, 0);

  /** 0111 1101 1101 cond  BMcnd C  */

  if (condition_true (cond))
    set_c (1);
  else
    set_c (0);

  /** 0111 1110 0101 srcx  BNAND src */

  BIT_OPC (srcx, (!b) & carry);

  /** 0111 1110 0111 srcx  BNOR src */

  BIT_OPC (srcx, (!b) | carry);

  /** 0111 1110 1010 dest  BNOT:G dest */

  BIT_OP (dest, !b);

  /** 0101 0bit            BNOT:S bit,base:11[SB] */

  dc = decode_bit11 (bit);
  put_bit (dc, !get_bit (dc));

  /** 0111 1110 0011 srcx  BNTST src */

  dc = decode_bit (srcx);
  b = get_bit (dc);
  set_zc (!b, !b);

  /** 0111 1110 1101 srcx  BNXOR src */

  BIT_OPC (srcx, !b ^ carry);

  /** 0111 1110 0110 srcx  BOR src */

  BIT_OPC (srcx, b | carry);

  /** 0000 0000            BRK */

  /* We report the break to our caller with the PC still pointing at the 
     breakpoint instruction.  */
  put_reg (pc, orig_pc);
  if (verbose)
    printf("[break]\n");
  return M32C_MAKE_HIT_BREAK ();

  /** 0111 1110 1001 dest  BSET:G dest */

  dc = decode_bit (dest);
  put_bit (dc, 1);

  /** 0100 1bit            BSET:S bit,base:11[SB] */

  dc = decode_bit11 (bit);
  put_bit (dc, 1);

  /** 0111 1110 1011 srcx  BTST:G src */

  dc = decode_bit (srcx);
  b = get_bit (dc);
  set_zc (!b, b);

  /** 0101 1bit            BTST:S bit,base:11[SB] */

  dc = decode_bit11 (bit);
  b = get_bit (dc);
  set_zc (!b, b);

  /** 0111 1110 0000 dest  BTSTC dest */

  dc = decode_bit (dest);
  b = get_bit (dc);
  set_zc (!b, b);
  put_bit (dc, 0);

  /** 0111 1110 0001 dest  BTSTS dest */

  dc = decode_bit (dest);
  b = get_bit (dc);
  set_zc (!b, b);
  put_bit (dc, 1);

  /** 0111 1110 1100 srcx  BXOR src */

  BIT_OPC (srcx, b ^ carry);

  /** 0111 011w 1000 dest  CMP.size:G #IMM,dest */

  UNARY_UOP;
  imm = IMM(w);
  cmp (v, imm, w);

  /** 1101 000w immm dest  CMP.size:Q #IMM,dest */

  UNARY_UOP;
  immm = sign_ext (immm, 4);
  cmp (v, immm, w);

  /** 1110 0dst            CMP.B:S #IMM8,dest */

  imm = IMM(0);
  dc = decode_dest3 (dst, 0);
  v = get_src (dc);
  cmp (v, imm, 0);

  /** 1100 000w srcx dest  CMP.size:G src,dest */

  BINARY_UOP;
  cmp(b, a, w);

  /** 0011 1d sr           CMP.B:S src,R0L/R0H */

  sc = decode_src2 (sr, 0, d);
  dc = decode_dest1 (d, 0);
  a = get_src (sc);
  b = get_src (dc);
  cmp (b, a, 0);

  /** 0111 110w 1110 i1c s  DADC,DADD,DSBB,DSUB */

  /* w = width, i = immediate, c = carry, s = subtract */

  {
  int src = i ? IMM(w) : get_reg (w ? r1 : r0h);
  int dest = get_reg (w ? r0 : r0l);
  int res;

  src = bcd2int(src, w);
  dest = bcd2int(dest, w);

  tprintf("decimal: %d %s %d", dest, s?"-":"+", src);
  if (c)
    tprintf(" c=%d", carry);

  if (!s)
    {
      res = dest + src;
      if (c)
	res += carry;
      c = res > (w ? 9999 : 99);
    }
  else
    {
      res = dest - src;
      if (c)
	res -= (1-carry);
      c = res >= 0;
      if (res < 0)
	res += w ? 10000 : 100;
    }

  res = int2bcd (res, w);
  tprintf(" = %x\n", res);

  set_szc (res, w+1, c);

  put_reg (w ? r0 : r0l, res);
  }

  /** 1010 1dst            DEC.B dest */

  dc = decode_dest3 (dst, 0);
  v = get_src (dc);
  tprintf("%x -- = %x\n", v, v-1);
  v --;
  set_sz (v, 1);
  put_dest (dc, v);

  /** 1111 d010            DEC.W dest */

  v = get_reg (d ? a1 : a0);
  tprintf("%x -- = %x\n", v, v-1);
  v --;
  set_sz (v, 2);
  put_reg (d ? a1 : a0, v);

  /** 0111 110w 1110 0001  DIV.size #IMM */

  div_op (-1, 0, 0, w);

  /** 0111 011w 1101 srcx  DIV.size src */

  div_op (srcx, 0, 0, w);

  /** 0111 110w 1110 0000  DIVU.size #IMM */

  div_op (-1, 1, 0, w);

  /** 0111 011w 1100 srcx  DIVU.size src */

  div_op (srcx, 1, 0, w);

  /** 0111 110w 1110 0011  DIVX.size #IMM */

  div_op (-1, 0, 1, w);

  /** 0111 011w 1001 srcx  DIVX.size src */

  div_op (srcx, 0, 1, w);

  /** 0111 1100 1111 0010  ENTER #IMM8 */

  imm = IMM(0);
  put_reg (sp, get_reg (sp) - 2);
  mem_put_hi (get_reg (sp), get_reg (fb));
  put_reg (fb, get_reg (sp));
  put_reg (sp, get_reg (sp) - imm);

  /** 0111 1101 1111 0010  EXITD */

  put_reg (sp, get_reg (fb));
  put_reg (fb, mem_get_hi (get_reg (sp)));
  put_reg (sp, get_reg (sp) + 2);
  put_reg (pc, mem_get_psi (get_reg (sp)));
  put_reg (sp, get_reg (sp) + 3);

  /** 0111 1100 0110 dest  EXTS.B dest */

  dc = decode_srcdest4 (dest, 0);
  v = sign_ext (get_src (dc), 8);
  dc = widen_sd (dc);
  put_dest (dc, v);
  set_sz (v, 1);

  /** 0111 1100 1111 0011  EXTS.W R0 */

  v = sign_ext (get_reg (r0), 16);
  put_reg (r2r0, v);
  set_sz (v, 2);

  /** 1110 1011 0flg 0101  FCLR dest */

  set_flags (1 << flg, 0);

  /** 1110 1011 0flg 0100  FSET dest */

  set_flags (1 << flg, 1 << flg);

  /** 1010 0dst            INC.B dest */

  dc = decode_dest3 (dst, 0);
  v = get_src (dc);
  tprintf("%x ++ = %x\n", v, v+1);
  v ++;
  set_sz (v, 1);
  put_dest (dc, v);

  /** 1011 d010            INC.W dest */

  v = get_reg (d ? a1 : a0);
  tprintf("%x ++ = %x\n", v, v+1);
  v ++;
  set_sz (v, 2);
  put_reg (d ? a1 : a0, v);

  /** 1110 1011 11vector   INT #imm */

  trigger_based_interrupt (vector);

  /** 1111 0110            INTO */

  if (FLAG_O)
    trigger_fixed_interrupt (0xffe0);

  /** 0110 1cnd            Jcnd label */

  v = sign_ext (IMM(0), 8);
  if (condition_true (cnd))
    put_reg (pc, orig_pc + 1 + v);

  /** 0111 1101 1100 cond  Jcnd label */

  v = sign_ext (IMM(0), 8);
  if (condition_true (cond))
    put_reg (pc, orig_pc + 2 + v);

  /** 0110 0dsp            JMP.S label */

  put_reg (pc, orig_pc + 2 + dsp);

  /** 1111 1110            JMP.B label */

  imm = sign_ext (IMM(0), 8);
  if (imm == -1)
    {
      if (verbose)
	printf("[jmp-to-self detected as exit]\n");
      return M32C_MAKE_HIT_BREAK ();
    }
  put_reg (pc, orig_pc + 1 + imm);

  /** 1111 0100            JMP.W label */

  imm = sign_ext (IMM(1), 16);
  put_reg (pc, orig_pc + 1 + imm);

  /** 1111 1100            JMP.A label */
  
  imm = IMM(2);
  put_reg (pc, imm);

  /** 0111 1101 0010 srcx  JMPI.W src */

  sc = decode_jumpdest (srcx, 1);
  a = get_src (sc);
  a = sign_ext (a, 16);
  put_reg (pc, orig_pc + a);

  /** 0111 1101 0000 srcx  JMPI.A src */

  sc = decode_jumpdest (srcx, 0);
  a = get_src (sc);
  put_reg (pc, a);

  /** 1110 1110            JMPS #IMM8 */

  M16C_ONLY();

  imm = IMM(0);
  a = 0xf0000 + mem_get_hi (0xffffe - imm * 2);
  put_reg (pc, a);

  /** 1111 0101            JSR.W label */

  imm = sign_ext (IMM(1), 16);
  put_reg (sp, get_reg (sp) - 3);
  mem_put_psi (get_reg (sp), get_reg (pc));
  put_reg (pc, orig_pc + imm + 1);

  /** 1111 1101            JSR.A label */

  imm = IMM(2);
  put_reg (sp, get_reg (sp) - 3);
  mem_put_psi (get_reg (sp), get_reg (pc));
  put_reg (pc, imm);

  /** 0111 1101 0011 srcx  JSRI.W src */

  sc = decode_jumpdest (srcx, 1);
  a = get_src (sc);
  a = sign_ext (a, 16);

  put_reg (sp, get_reg (sp) - 3);
  mem_put_psi (get_reg (sp), get_reg (pc));
  put_reg (pc, orig_pc + a);

  /** 0111 1101 0001 srcx  JSRI.A src */

  sc = decode_jumpdest (srcx, 0);
  a = get_src (sc);

  put_reg (sp, get_reg (sp) - 3);
  mem_put_psi (get_reg (sp), get_reg (pc));
  put_reg (pc, a);

  /** 1110 1111            JSRS #IMM8 */

  M16C_ONLY();

  imm = IMM(0);
  a = 0xf0000 + mem_get_hi (0xffffe - imm * 2);

  put_reg (sp, get_reg (sp) - 3);
  mem_put_psi (get_reg (sp), get_reg (pc));
  put_reg (pc, a);

  /** 1110 1011 0reg 0000  LDC #IMM16,dest */

  dc = decode_cr (reg);
  imm = IMM(1);
  put_dest (dc, imm);

  /** 0111 1010 1reg srcx  LDC src,dest */

  dc = decode_cr (reg);
  sc = decode_srcdest4 (srcx,1);
  put_dest (dc, get_src (sc));

  /** 0111 1100 1111 0000  LDCTX abs16,abs20 */

  NOTYET();

  /** 0111 010w 1000 dest  LDE.size abs20,dest */

  dc = decode_srcdest4 (dest, w);
  imm = IMM(2);
  if (w)
    v = mem_get_hi (imm);
  else
    v = mem_get_qi (imm);
  put_dest (dc, v);

  /** 0111 010w 1001 dest  LDE.size dsp:20[a0], dest */

  dc = decode_srcdest4 (dest, w);
  imm = IMM(2) + get_reg (a0);
  if (w)
    v = mem_get_hi (imm);
  else
    v = mem_get_qi (imm);
  put_dest (dc, v);

  /** 0111 010w 1010 dest  LDE.size [a1a0],dest */
  
  dc = decode_srcdest4 (dest, w);
  imm = get_reg (a1a0);
  if (w)
    v = mem_get_hi (imm);
  else
    v = mem_get_qi (imm);
  put_dest (dc, v);

  /** 0111 1101 1010 0flg  LDIPL #IMM */

  set_flags (0x700, flg*0x100);

  /** 0111 010w 1100 dest  MOV.size:G #IMM,dest */

  dc = decode_srcdest4 (dest, w);
  imm = IMM(w);
  v = imm;
  tprintf("%x = %x\n", v, v);
  set_sz(v, w+1);
  put_dest (dc, v);

  /** 1101 100w immm dest  MOV.size:Q #IMM,dest */

  dc = decode_srcdest4 (dest, w);
  v = sign_ext (immm, 4);
  tprintf ("%x = %x\n", v, v);
  set_sz (v, w+1);
  put_dest (dc, v);

  /** 1100 0dst            MOV.B:S #IMM8,dest */

  imm = IMM(0);
  dc = decode_dest3 (dst, 0);
  v = imm;
  tprintf("%x = %x\n", v, v);
  set_sz (v, 1);
  put_dest (dc, v);

  /** 1w10 d010            MOV.size:S #IMM,dest */

  /* Note that for w, 0=W and 1=B unlike the usual meaning.  */
  v = IMM(1-w);
  tprintf("%x = %x\n", v, v);
  set_sz (v, 2-w);
  put_reg (d ? a1 : a0, v);

  /** 1011 0dst            MOV.B:Z #0,dest */

  dc = decode_dest3 (dst, 0);
  v = 0;
  set_sz (v, 1);
  put_dest (dc, v);

  /** 0111 001w srcx dest  MOV.size:G src,dest */

  sc = decode_srcdest4 (srcx, w);
  dc = decode_srcdest4 (dest, w);
  v = get_src (sc);
  set_sz (v, w+1);
  put_dest (dc, v);

  /** 0011 0d sr           MOV.B:S src,dest */

  sc = decode_src2 (sr, 0, d);
  v = get_src (sc);
  set_sz (v, 1);
  put_reg (d ? a1 : a0, v);

  /** 0000 0s ds           MOV.B:S R0L/R0H,dest */

  if (ds == 0)
    UNSUPPORTED();
  dc = decode_src2 (ds, 0, s);
  v = get_reg (s ? r0h : r0l);
  set_sz (v, 1);
  put_dest (dc, v);

  /** 0000 1d sr           MOV.B:S src,R0L/R0H */

  sc = decode_src2 (sr, 0, d);
  v = get_src (sc);
  set_sz (v, 1);
  put_reg (d ? r0h : r0l, v);

  /** 0111 010w 1011 dest  MOV.size:G dsp:8[SP], dest */

  dc = decode_srcdest4 (dest, w);
  imm = IMM(0);
  a = get_reg (sp) + sign_ext (imm, 8);
  a &= addr_mask;
  if (w)
    v = mem_get_hi (a);
  else
    v = mem_get_qi (a);
  set_sz (v, w+1);
  put_dest (dc, v);

  /** 0111 010w 0011 srcx  MOV.size:G src, disp8[SP] */

  sc = decode_srcdest4 (srcx, w);
  imm = IMM(0);
  a = get_reg (sp) + sign_ext (imm, 8);
  a &= addr_mask;
  v = get_src (sc);
  if (w)
    mem_put_hi (a, v);
  else
    mem_put_qi (a, v);
  set_sz (v, w+1);

  /** 1110 1011 0reg 1src  MOVA src,dest */

  {
  static reg_id map[] = { r0, r1, r2, r3, a0, a1, 0, 0 };
  sc = decode_srcdest4 (8 + src, 0);
  put_reg (map[reg], sc.u.addr);
  }

  /** 0111 1100 10hl dest  MOVdir R0L,dest */

  if (dest == 0 || dest == 4 || dest == 5)
    UNSUPPORTED();
  dc = decode_srcdest4 (dest, 0);
  a = get_src (dc);
  b = get_reg (r0l);
  switch (hl)
    {
    case 0: a = (a & 0xf0) | (b & 0x0f); break;
    case 1: a = (a & 0xf0) | ((b>>4) & 0x0f); break;
    case 2: a = (a & 0x0f) | ((b & 0x0f)<<4); break;
    case 3: a = (a & 0x0f) | (b & 0xf0); break;
    }
  put_dest (dc, a);

  /** 0111 1100 00hl srcx  MOVdir src,R0L */

  if (srcx == 0 || srcx == 4 || srcx == 5)
    UNSUPPORTED();
  sc = decode_srcdest4 (srcx, 0);
  a = get_reg (r0l);
  b = get_src (sc);
  switch (hl)
    {
    case 0: a = (a & 0xf0) | (b & 0x0f); break;
    case 1: a = (a & 0xf0) | ((b>>4) & 0x0f); break;
    case 2: a = (a & 0x0f) | ((b & 0x0f)<<4); break;
    case 3: a = (a & 0x0f) | (b & 0xf0); break;
    }
  put_reg (r0l, a);

  /** 0111 110w 0101 dest  MUL.size #IMM,dest */

  UNARY_SOP;
  imm = sign_ext (IMM(w), w?16:8);
  tprintf("%d * %d = %d\n", v, imm, v*imm);
  v *= imm;
  dc = widen_sd (dc);
  put_dest (dc, v);

  /** 0111 100w srcx dest  MUL.size src,dest */

  BINARY_SOP;
  v = a * b;
  tprintf("%d * %d = %d\n", a, b, v);
  dc = widen_sd (dc);
  put_dest (dc, v);

  /** 0111 110w 0100 dest  MULU.size #IMM,dest */

  UNARY_UOP;
  imm = IMM(w);
  tprintf("%u * %u = %u\n", v, imm, v*imm);
  v *= imm;
  dc = widen_sd (dc);
  put_dest (dc, v);

  /** 0111 000w srcx dest  MULU.size src,dest */

  BINARY_UOP;
  v = a * b;
  tprintf("%u * %u = %u\n", a, b, v);
  dc = widen_sd (dc);
  put_dest (dc, v);

  /** 0111 010w 0101 dest  NEG.size dest */

  UNARY_SOP;
  tprintf("%d * -1 = %d\n", v, -v);
  v = -v;
  set_oszc (v, w+1, v == 0);
  put_dest (dc, v);

  /** 0000 0100            NOP */

  tprintf("nop\n");

  /** 0111 010w 0111 dest  NOT.size:G */

  UNARY_UOP;
  tprintf("~ %x = %x\n", v, ~v);
  v = ~v;
  set_sz (v, w+1);
  put_dest (dc, v);

  /** 1011 1dst            NOT.B:S dest */

  dc = decode_dest3 (dst, 0);
  v = get_src (dc);
  tprintf("~ %x = %x\n", v, ~v);
  v = ~v;
  set_sz (v, 1);
  put_dest (dc, v);

  /** 0111 011w 0011 dest  OR.size:G #IMM,dest */

  UNARY_UOP;
  imm = IMM(w);
  tprintf ("%x | %x = %x\n", v, imm, v | imm);
  v |= imm;
  set_sz (v, w+1);
  put_dest (dc, v);

  /** 1001 1dst            OR.B:S #IMM8,dest */

  imm = IMM(0);
  dc = decode_dest3 (dst, 0);
  v = get_src (dc);
  tprintf("%x | %x = %x\n", v, imm, v|imm);
  v |= imm;
  set_sz (v, 1);
  put_dest (dc, v);

  /** 1001 100w srcx dest  OR.size:G src,dest */

  BINARY_UOP;
  tprintf ("%x | %x = %x\n", a, b, a | b);
  v = a | b;
  set_sz (v, w+1);
  put_dest (dc, v);

  /** 0001 1d sr           OR.B:S src,R0L/R0H */

  sc = decode_src2 (sr, 0, d);
  dc = decode_dest1 (d, 0);
  a = get_src (sc);
  b = get_src (dc);
  v = a | b;
  tprintf("%x | %x = %x\n", a, b, v);
  set_sz (v, 1);
  put_dest (dc, v);

  /** 0111 010w 1101 dest  POP.size:G dest */

  dc = decode_srcdest4 (dest, w);
  if (w)
    {
      v = mem_get_hi (get_reg (sp));
      put_reg (sp, get_reg (sp) + 2);
      tprintf("pophi: %x\n", v);
    }
  else
    {
      v = mem_get_qi (get_reg (sp));
      put_reg (sp, get_reg (sp) + 1);
      tprintf("popqi: %x\n", v);
    }
  put_dest (dc, v);

  /** 1001 d010            POP.B:S dest */

  v = mem_get_qi (get_reg (sp));
  put_reg (d ? r0h : r0l, v);
  put_reg (sp, get_reg (sp) + 1);
  tprintf("popqi: %x\n", v);

  /** 1101 d010            POP.W:S dest */

  v = mem_get_hi (get_reg (sp));
  put_reg (d ? a1 : a0, v);
  put_reg (sp, get_reg (sp) + 2);
  tprintf("pophi: %x\n", v);

  /** 1110 1011 0reg 0011  POPC dest */

  dc = decode_cr (reg);
  v = mem_get_hi (get_reg (sp));
  put_dest (dc, v);
  put_reg (sp, get_reg (sp) + 2);
  tprintf("popc: %x\n", v);

  /** 1110 1101            POPM dest */

  {
  static int map[] = { r0, r1, r2, r3, a0, a1, sb, fb };
  imm = IMM(0);
  tprintf("popm: %x\n", imm);
  for (a=0; a<8; a++)
    if (imm & (1<<a))
      {
	v = mem_get_hi (get_reg (sp));
	put_reg (map[a], v);
	put_reg (sp, get_reg (sp) + 2);
      }
  }

  /** 0111 110w 1110 0010  PUSH.size:G #IMM */

  imm = IMM(w);
  if (w)
    {
      put_reg (sp, get_reg (sp) - 2);
      mem_put_hi (get_reg (sp), imm);
      tprintf("pushhi %04x\n", imm);
    }
  else
    {
      put_reg (sp, get_reg (sp) - 1);
      mem_put_qi (get_reg (sp), imm);
      tprintf("pushqi %02x\n", imm);
    }

  /** 0111 010w 0100 srcx  PUSH.size:G src */

  sc = decode_srcdest4 (srcx, w);
  v = get_src (sc);
  if (w)
    {
      put_reg (sp, get_reg (sp) - 2);
      mem_put_hi (get_reg (sp), v);
      tprintf("pushhi: %x\n", v);
    }
  else
    {
      put_reg (sp, get_reg (sp) - 1);
      mem_put_qi (get_reg (sp), v);
      tprintf("pushqi: %x\n", v);
    }

  /** 1000 s010            PUSH.B:S src */

  v = get_reg (s ? r0h : r0l);
  put_reg (sp, get_reg (sp) - 1);
  mem_put_qi (get_reg (sp), v);
  tprintf("pushqi: %x\n", v);

  /** 1100 s010            PUSH.W:S src */

  v = get_reg (s ? a1 : a0);
  put_reg (sp, get_reg (sp) - 2);
  mem_put_hi (get_reg (sp), v);
  tprintf("pushhi: %x\n", v);

  /** 0111 1101 1001 srcx  PUSHA src */

  sc = decode_srcdest4 (srcx, 0);
  put_reg (sp, get_reg (sp) - 2);
  mem_put_hi (get_reg (sp), sc.u.addr);
  tprintf("pushhi: %x\n", sc.u.addr);

  /** 1110 1011 0src 0010  PUSHC src */

  sc = decode_cr (src);
  put_reg (sp, get_reg (sp) - 2);
  v = get_src (sc);
  mem_put_hi (get_reg (sp), v);
  tprintf("pushc: %x\n", v);

  /** 1110 1100            PUSHM src */

  {
  static int map[] = { fb, sb, a1, a0, r3, r2, r1, r0 };
  imm = IMM(0);
  tprintf("pushm: %x\n", imm);
  for (a=0; a<8; a++)
    if (imm & (1<<a))
      {
	put_reg (sp, get_reg (sp) - 2);
	v = get_reg (map[a]);
	mem_put_hi (get_reg (sp), v);
      }
  }

  /** 1111 1011            REIT */

  a = get_reg (sp);
  v = (mem_get_hi (a)
       + 4096 * (mem_get_qi (a+3) & 0xf0));
  b = (mem_get_qi (a+2)
       + 256 * (mem_get_qi (a+3) & 0xff));
  put_reg (pc, v);
  put_reg (flags, b);
  put_reg (sp, get_reg (sp) + 4);

  /** 0111 110w 1111 0001  RMPA.size */

  {
  int count = get_reg (r3);
  int list1 = get_reg (a0);
  int list2 = get_reg (a1);
  int sum = get_reg (w ? r2r0 : r0);

  while (count)
    {
      if (w)
	{
	  a = sign_ext (mem_get_hi (list1), 16);
	  b = sign_ext (mem_get_hi (list2), 16);
	}
      else
	{
	  a = sign_ext (mem_get_qi (list1), 8);
	  b = sign_ext (mem_get_qi (list2), 8);
	}
      tprintf("%d + %d * %d = ", sum, a, b);
      sum += a * b;
      tprintf("%d\n", sum);
      list1 += w ? 2 : 1;
      list2 += w ? 2 : 1;
      count --;
    }
  put_reg (r3, count);
  put_reg (a0, list1);
  put_reg (a1, list2);
  put_reg (w ? r2r0 : r0, sum);
  }

  /** 0111 011w 1010 dest  ROLC.size dest */

  dc = decode_srcdest4 (dest, w);
  rot_op (dc, 1, 1);

  /** 0111 011w 1011 dest  RORC.size dest */

  dc = decode_srcdest4 (dest, w);
  rot_op (dc, 1, -1);

  /** 1110 000w immm dest  ROT.size #IMM,dest */

  dc = decode_srcdest4 (dest, w);
  rot_op (dc, 0, IMM4());

  /** 0111 010w 0110 dest  ROT.size R1H,dest */

  dc = decode_srcdest4 (dest, w);
  rot_op (dc, 0, sign_ext (get_reg (r1h), 8));

  /** 1111 0011            RTS */

  put_reg (pc, mem_get_psi (get_reg (sp)));
  put_reg (sp, get_reg (sp) + 3);

  /** 0111 011w 0111 dest  SBB.size #IMM,dest */

  dc = decode_srcdest4 (dest, w);
  imm = IMM(w);
  MATH_OP (dc, imm, !carry, -, >= 0);

  /** 1011 100w srcx dest  SBB.size src,dest */

  sc = decode_srcdest4(srcx, w);
  dc = decode_srcdest4(dest, w);
  b = get_src (sc);
  MATH_OP (dc, b, !carry, -, >= 0);

  /** 1111 000w immm dest  SHA.size #IMM, dest */

  dc = decode_srcdest4(dest, w);
  shift_op (dc, 1, IMM4());

  /** 0111 010w 1111 dest  SHA.size R1H,dest */

  dc = decode_srcdest4(dest, w);
  a = sign_ext (get_reg (r1h), 8);
  shift_op (dc, 1, a);

  /** 1110 1011 101d immm  SHA.L #IMM, dest */

  dc = reg_sd (d ? r3r1 : r2r0);
  shift_op (dc, 1, IMM4());

  /** 1110 1011 001d 0001  SHA.L R1H,dest */

  dc = reg_sd (d ? r3r1 : r2r0);
  a = sign_ext (get_reg (r1h), 8);
  shift_op (dc, 1, a);

  /** 1110 100w immm dest  SHL.size #IMM, dest */

  dc = decode_srcdest4(dest, w);
  shift_op (dc, 0, IMM4());

  /** 0111 010w 1110 dest  SHL.size R1H,dest */

  dc = decode_srcdest4(dest, w);
  a = sign_ext (get_reg (r1h), 8);
  shift_op (dc, 0, a);

  /** 1110 1011 100d immm  SHL.L #IMM,dest */

  dc = reg_sd (d ? r3r1 : r2r0);
  shift_op (dc, 0, IMM4());

  /** 1110 1011 000d 0001  SHL.L R1H,dest */

  dc = reg_sd (d ? r3r1 : r2r0);
  a = sign_ext (get_reg (r1h), 8);
  shift_op (dc, 0, a);

  /** 0111 110w 1110 100z  SMOVB.size */

  {
  int count = get_reg (r3);
  int s1 = get_reg (a0) + (get_reg (r1h) << 16);
  int s2 = get_reg (a1);
  int inc = (w ? 2 : 1) * (z ? -1 : 1);

  while (count)
    {
      if (w)
	{
	  v = mem_get_hi (s1);
	  mem_put_hi (s2, v);
	}
      else
	{
	  v = mem_get_qi (s1);
	  mem_put_qi (s2, v);
	}
      s1 += inc;
      s2 += inc;
      count --;
    }
  put_reg (r3, count);
  put_reg (a0, s1 & 0xffff);
  put_reg (a1, s2);
  put_reg (r1h, s1 >> 16);
  }

  /** 0111 110w 1110 1010  SSTR.size */

  {
  int count = get_reg (r3);
  int s1 = get_reg (a1);
  v = get_reg (w ? r0 : r0l);

  while (count)
    {
      if (w)
	{
	  mem_put_hi (s1, v);
	  s1 += 2;
	}
      else
	{
	  mem_put_qi (s1, v);
	  s1 += 1;
	}
      count --;
    }
  put_reg (r3, count);
  put_reg (a1, s1);
  }

  /** 0111 1011 1src dest  STC src,dest */

  dc = decode_srcdest4 (dest, 1);
  sc = decode_cr (src);
  put_dest (dc, get_src(sc));

  /** 0111 1100 1100 dest  STC PC,dest */

  dc = decode_srcdest4 (dest, 1);
  dc.bytes = 3;
  put_dest (dc, orig_pc);

  /** 0111 1101 1111 0000  STCTX abs16,abs20 */

  NOTYET();

  /** 0111 010w 0000 srcx  STE.size src,abs20 */

  sc = decode_srcdest4 (srcx, w);
  a = IMM(2);
  v = get_src (sc);
  if (w)
    mem_put_hi (a, v);
  else
    mem_put_qi (a, v);
  if (srcx == 4 || srcx == 5)
    {
      v = get_reg (sc.u.reg);
      set_sz (v, 2);
    }
  else
    set_sz (v, w+1);
    
  /** 0111 010w 0001 srcx  STE.size src,disp20[a0] */

  sc = decode_srcdest4 (srcx, w);
  a = get_reg(a0) + IMM(2);
  v = get_src (sc);
  if (w)
    mem_put_hi (a, v);
  else
    mem_put_qi (a, v);
  if (srcx == 4 || srcx == 5)
    {
      v = get_reg (sc.u.reg);
      set_sz (v, 2);
    }
  else
    set_sz (v, w+1);

  /** 0111 010w 0010 srcx  STE.size src,[a1a0] */

  sc = decode_srcdest4 (srcx, w);
  a = get_reg(a1a0);
  v = get_src (sc);
  if (w)
    mem_put_hi (a, v);
  else
    mem_put_qi (a, v);
  if (srcx == 4 || srcx == 5)
    {
      v = get_reg (sc.u.reg);
      set_sz (v, 2);
    }
  else
    set_sz (v, w+1);

  /** 1101 0dst            STNZ #IMM8,dest */

  imm = IMM(0);
  dc = decode_dest3(dst, 0);
  if (!FLAG_Z)
    put_dest (dc, imm);

  /** 1100 1dst            STZ #IMM8,dest */

  imm = IMM(0);
  dc = decode_dest3(dst, 0);
  if (FLAG_Z)
    put_dest (dc, imm);

  /** 1101 1dst            STZX #IMM81,#IMM82,dest */

  a = IMM(0);
  dc = decode_dest3(dst, 0);
  b = IMM(0);
  if (FLAG_Z)
    put_dest (dc, a);
  else
    put_dest (dc, b);

  /** 0111 011w 0101 dest  SUB.size:G #IMM,dest */

  dc = decode_srcdest4 (dest, w);
  imm = IMM(w);
  MATH_OP (dc, imm, 0, -, >= 0);

  /** 1000 1dst            SUB.B:S #IMM8,dest */

  imm = IMM(0);
  dc = decode_dest3 (dst, 0);
  MATH_OP (dc, imm, 0, -, >= 0);

  /** 1010 100w srcx dest  SUB.size:G src,dest */

  sc = decode_srcdest4(srcx, w);
  dc = decode_srcdest4(dest, w);
  b = get_src (sc);
  MATH_OP (dc, b, 0, -, >= 0);

  /** 0010 1d sr           SUB.B:S src,R0L/R0H */

  sc = decode_src2 (sr, 0, d);
  dc = decode_dest1 (d, 0);
  b = get_src (sc);
  MATH_OP (dc, b, 0, -, >= 0);

  /** 0111 011w 0000 dest  TST.size #IMM, dest */

  UNARY_UOP;
  imm = IMM(w);
  tprintf ("%x & %x = %x\n", v, imm, v & imm);
  v &= imm;
  set_sz (v, w+1);

  /** 1000 000w srcx dest  TST.size src,dest */

  BINARY_UOP;
  tprintf ("%x & %x = %x\n", a, b, a & b);
  v = a & b;
  set_sz (v, w+1);

  /** 1111 1111            UND */

  trigger_fixed_interrupt (0xffdc);

  /** 0111 1101 1111 0011  WAIT */

  tprintf("waiting...\n");

  /** 0111 101w 00sr dest  XCHG.size src,dest */

  sc = decode_srcdest4 (sr, w);
  dc = decode_srcdest4 (dest, w);
  a = get_src (sc);
  b = get_src (dc);
  put_dest (dc, a);
  put_dest (sc, b);

  /** 0111 011w 0001 dest  XOR.size #IMM,dest */

  UNARY_UOP;
  imm = IMM(w);
  tprintf ("%x ^ %x = %x\n", v, imm, v ^ imm);
  v ^= imm;
  set_sz (v, w+1);
  put_dest (dc, v);

  /** 1000 100w srcx dest  XOR.size src,dest */

  BINARY_UOP;
  tprintf ("%x ^ %x = %x\n", a, b, a ^ b);
  v = a ^ b;
  set_sz (v, w+1);
  put_dest (dc, v);
  
  /**                      OP */
/** */

  return step_result;
}
